#pragma once
#include <Rz/IObject.h>
#include <Rz/ITypeWrapper.h>
#include <Rz/Collections/Iterator.h>

// const
#define RZ_ICONSTENUMERABLE_IT_TO_PTR(o, it)				\
	typename ::Rz::Collections::IEnumerable<T>::ConstIterator(typename ::Rz::Collections::IEnumerable<T>::ConstIterator::Pointer(!(o).size() ? nullptr : &(o)[0] + ((it) - (o).begin())))

#define RZ_ICONSTENUMERABLE_IMPLEMENT_DEFAULT(member)															\
	RZ_INLINE virtual typename ::Rz::Collections::IEnumerable<T>::ConstIterator ConstBegin() const override	{ return RZ_ICONSTENUMERABLE_IT_TO_PTR(member, member.begin()); }	\
	RZ_INLINE virtual typename ::Rz::Collections::IEnumerable<T>::ConstIterator ConstEnd()   const override	{ return RZ_ICONSTENUMERABLE_IT_TO_PTR(member, member.end());   }

// nonconst
#define RZ_IENUMERABLE_IT_TO_PTR(o, it)						\
	typename ::Rz::Collections::IEnumerable<T>::Iterator(typename ::Rz::Collections::IEnumerable<T>::Iterator::Pointer(!(o).size() ? nullptr : &(o)[0] + ((it) - (o).begin())))

#define RZ_IENUMERABLE_IMPLEMENT_DEFAULT(member)													\
	RZ_INLINE virtual typename ::Rz::Collections::IEnumerable<T>::Iterator Begin() override { return RZ_IENUMERABLE_IT_TO_PTR(member, member.begin()); }			\
	RZ_INLINE virtual typename ::Rz::Collections::IEnumerable<T>::Iterator End() override { return RZ_IENUMERABLE_IT_TO_PTR(member, member.end()); }			\
	RZ_ICONSTENUMERABLE_IMPLEMENT_DEFAULT(member)

namespace Rz { namespace Collections {

template <typename T, typename TConstIt = TConstIterator<T>, typename TIt = TIterator<T>>
class IEnumerable :
	virtual public IObject,
	virtual public ITypeWrapper<T>
{
	public:
		RZ_RTTI_INTERFACE(Rz::Collections, IEnumerable);
		RZ_RTTI_IMPLEMENTS(IObject, ITypeWrapper<T>);
		RZ_RTTI_TEMPLATES(NonObjectType<T>);
		RZ_RTTI_END;
		
		typedef TConstIt	ConstIterator;
		typedef TIt			Iterator;

		// const
		virtual ConstIterator begin()	const	{ return ConstBegin();	}
		virtual ConstIterator end()		const	{ return ConstEnd();	}

		virtual ConstIterator ConstBegin()	const	= 0;
		virtual ConstIterator ConstEnd()	const	= 0;

		// non-const
		virtual Iterator begin()	{ return Begin();	}
		virtual Iterator end()		{ return End();		}

		virtual Iterator Begin()	= 0;
		virtual Iterator End()		= 0;
};

} }
